package jobs

import (
	"fmt"
	"gitee.com/wm1998/resk/core/envelopes"

	// redsync是Redis官方推荐，基于redis set命令实现的分布式锁的开源组件
	"gitee.com/wm1998/infra"
	"github.com/go-redsync/redsync"
	"github.com/gomodule/redigo/redis"
	log "github.com/sirupsen/logrus"
	"github.com/tietang/go-utils"
	"time"
)

// 红包过期检测定时任务
type RefundExpiredJobStarter struct {
	infra.BaseStarter                // 包含infra.BaseStarter接口，目的是可以选择性地实现该接口方法
	ticker            *time.Ticker   // 基于Golang内置的定时任务
	mutex             *redsync.Mutex // 使用redsync.Mutex互斥锁的Lock和Unlock这2个方法来拿锁和释放锁
}

// 实现了启动器管理器接口的此方法
func (r *RefundExpiredJobStarter) Init(ctx infra.StarterContext) {
	d := ctx.Props().GetDurationDefault("jobs.refund.interval", time.Minute)
	r.ticker = time.NewTicker(d)

	// 以下是使用基于Redis锁的分布式定时任务
	// 即：使用Redis官方推荐，基于redis set命令实现的分布式锁，以解决Redis Setnx命令实现分布式锁的缺点

	// 设置Resis连接池的相关配置项
	maxIdle := ctx.Props().GetIntDefault("redis.maxIdle", 2)                   // 最大空闲连接数
	maxActive := ctx.Props().GetIntDefault("redis.maxActive", 5)               // 最大活动连接数（连接池的最大可用连接数）
	timeout := ctx.Props().GetDurationDefault("redis.timeout", 20*time.Second) // 连接的超时时间：20秒
	addr := ctx.Props().GetDefault("redis.addr", "127.0.0.1:6379")             // Redis服务器IP和端口

	// 【1.构建一个redigo链接池对象】
	pools := make([]redsync.Pool, 0)
	pool := &redis.Pool{
		MaxIdle:     maxIdle,
		MaxActive:   maxActive,
		IdleTimeout: timeout,
		Dial: func() (conn redis.Conn, e error) {
			return redis.Dial("tcp", addr)
		},
	}
	pools = append(pools, pool)

	// 【2.创建一个redsync实例】
	rsync := redsync.New(pools)
	ip, err := utils.GetExternalIP()
	if err != nil {
		ip = "127.0.0.1"
	}

	// 【3.再通过redsync实例，创建mutex互斥锁实例】
	r.mutex = rsync.NewMutex("lock:RefundExpired",
		redsync.SetExpiry(50*time.Second), // 设置锁的key值过期时间：50秒
		redsync.SetRetryDelay(3),          // 重试3次拿不到锁的key值，则失败
		redsync.SetGenValueFunc(func() (s string, e error) { // 设置锁的key值
			now := time.Now()
			log.Infof("节点%s正在执行过期红包的退款任务", ip)
			return fmt.Sprintf("%d:%s", now.Unix(), ip), nil
		}),
	)
}

// 实现了启动器管理器接口的此方法
func (r *RefundExpiredJobStarter) Start(ctx infra.StarterContext) {
	go func() {
		for {
			c := <-r.ticker.C // 操作系统每隔1分钟会将时间戳写入到r.ticker.C里
			// log.Debug("过期红包退款开始...", c)
			// 红包过期退款的业务逻辑代码
			// 【4.使用mutex互斥锁的Lock和Unlock这2个方法来拿锁和释放锁】
			err := r.mutex.Lock()
			if err == nil {
				log.Debug("过期红包退款开始...", c)

				// 【红包过期退款的业务逻辑代码】
				domain := envelopes.ExpiredEnvelopeDomain{}
				domain.Expired()
			} else {
				log.Info("已经有节点在运行【过期红包退款的定时任务】了")
			}
			r.mutex.Unlock()
		}
	}()
}

// 实现了启动器管理器接口的此方法
func (r *RefundExpiredJobStarter) Stop(ctx infra.StarterContext) {
	r.ticker.Stop()
}

//这里把测试和debug留
